﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Kotonoha.Objects;
using Kotonoha.Server.Objects.Model;
using Kotonoha.Server.Extensions;

namespace Kotonoha.Server.Core.Xml {

    internal static class XmlWriterExtensions {
        internal static void WriteNotnullAttribute(this XmlWriter writer, string name, string value) {
            if (!string.IsNullOrEmpty(value)) {
                writer.WriteStartAttribute(name);
                writer.WriteString(value);
                writer.WriteEndAttribute();
            }
        }

        internal static void WriteNotnullAttribute(this XmlWriter writer, string s, long id) {
            writer.WriteNotnullAttribute(s, id.ToString());
        }

        internal static void WriteNotnullAttribute(this XmlWriter writer, string s, DateTime dt) {
            if (dt != null) {
                writer.WriteNotnullAttribute(s, dt.ToString("o"));
            }
        }
    }


    public class ContainerSerializer {

        static NumberFormatInfo format = new NumberFormatInfo();

        static ContainerSerializer() {
            format.CurrencyDecimalSeparator = ".";
        }

        public void Serialize(Stream s, Container c) {
            XmlWriter writer = createWriter(s);
            writer.WriteStartElement("Container");
            writeWords(writer, c);
            writeKanji(writer, c);
            writer.WriteEndElement();
            writer.Flush();
            writer.Close();
        }

        private void writeKanji(XmlWriter writer, Container c) {
            writer.WriteStartElement("Kanji");
            c.Kanji.ForEach(k => writeEachKanji(writer, k));
            writer.WriteEndElement();
        }

        private void writeEachKanji(XmlWriter writer, Kanji k) {
            writer.WriteStartElement("Kanji");

            writer.WriteNotnullAttribute("Id", k.Id);
            writer.WriteNotnullAttribute("Character", k.Character);
            writer.WriteNotnullAttribute("Strokes", k.Strokes);
            writer.WriteNotnullAttribute("AddedDate", k.AddedDate);

            writeStringCollection(writer, k.Onyomis, "Onyomis");
            writeStringCollection(writer, k.Kunyomis, "Kunyomis");
            writeStringCollection(writer, k.Meanings, "Meanings");

            writeAdditional(writer, k.Additional);

            writer.WriteEndElement();
        }
        
        private void writeWords(XmlWriter writer, Container c) {
            writer.WriteStartElement("Words");
            c.Words.ForEach(w => writeWord(writer, w));
            writer.WriteEndElement();
        }

        private void writeWord(XmlWriter writer, Word w) {
            writer.WriteStartElement("Word");
            writer.WriteNotnullAttribute("Writing", w.Writing);
            writer.WriteNotnullAttribute("Reading", w.Reading);
            writer.WriteNotnullAttribute("Id", w.Id);
            writer.WriteNotnullAttribute("AddedDate", w.AddedDate);

            writeStringCollection(writer, w.Translations, "Translations");

            writer.WriteStartElement("Examples");
            foreach (Example ex in w.Examples) {
                writer.WriteStartElement("Example");
                writeSimpleElement(writer, "Content", ex.Content);
                writeSimpleElement(writer, "Translation", ex.Translation);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();

            writer.WriteStartElement("Repetition");
            TimedVariable r = w.Repetition;
            writer.WriteNotnullAttribute("SetDate", r.SetDate);
            writer.WriteNotnullAttribute("InitialValue", r.InitialValue.ToString(format));
            writer.WriteNotnullAttribute("Rate", r.Rate.ToString(format));
            writer.WriteNotnullAttribute("Max", r.Max.ToString(format));
            writer.WriteNotnullAttribute("Min", r.Min.ToString(format));
            writer.WriteEndElement();

            writeAdditional(writer, w.Additional);
            writer.WriteEndElement();
        }

        private void writeStringCollection(XmlWriter writer, IEnumerable<string> w, string name) {
            writer.WriteStartElement(name);
            w.ForEach(s => writeSimpleElement(writer, "string", s));
            writer.WriteEndElement();
        }

        private void writeAdditional(XmlWriter writer, IDictionary<string,string> additional) {
            writer.WriteStartElement("Additional");
            foreach (var kvp in additional) {
                writer.WriteStartElement("item");
                writer.WriteStartElement("key");
                writeSimpleElement(writer, "string", kvp.Key);
                writer.WriteEndElement();
                writer.WriteStartElement("value");
                writeSimpleElement(writer, "string", kvp.Value);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }

        private void writeSimpleElement(XmlWriter writer, string name, string value) {
            writer.WriteStartElement(name);
            writer.WriteString(value);
            writer.WriteEndElement();
        }

        private static XmlWriter createWriter(Stream s) {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.CheckCharacters = false;
            settings.CloseOutput = false;
            settings.ConformanceLevel = ConformanceLevel.Document;
            settings.Encoding = Encoding.UTF8;
            settings.Indent = true;
            return XmlWriter.Create(s, settings);
        }
    }
}
